package com.lnyt.serviceImpl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.lnyt.dao.t12306.TicketTaskMapper;
import com.lnyt.dao.t12306.TicketStore;
import com.lnyt.pojo.ticket.TicketTask;
import com.lnyt.pojo.ticket.TicketUser;
import com.lnyt.pojo.ticket.TrainNumber;
import com.lnyt.service.TicketAutoService;
import com.lnyt.service.TicketReqService;
import com.lnyt.util.CheckUtil;
import com.lnyt.util.LogUtil;
import com.lnyt.util.MessageUtil;
import com.lnyt.util.TicketUtil;
import com.lnyt.util.factroy.TicketFactroy;
import okhttp3.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


@Service
@Transactional
public class TicketAutoServiceImpl extends Observable implements TicketAutoService {
    // Http请求服务
    @Resource
    private TicketReqService ticketReqService;
    // 缓存
    @Resource
    private TicketStore ticketStore;
    // 数据库Dao
    @Resource
    private TicketTaskMapper ticketTaskMapper;
    // Url表
    private Map<String, String> urlMap;
    // 读写锁
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();


    public Map<String, String> getUrlMap() {
        return urlMap;
    }

    public void setUrlMap(Map<String, String> urlMap) {
        this.urlMap = urlMap;
    }


    @Override
    public Observable selfObservable() {
        return this;
    }

    @Override
    public void initService() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);

        // 连接保持线程
        Runnable idle = new Runnable() {
            @Override
            public void run() {
                readWriteLock.readLock().lock();

                try {
                    idleFunc();
                } catch (Exception exc) {
                    exc.printStackTrace();
                } finally {
                    readWriteLock.readLock().unlock();
                }
            }
        };

        // 未完成订单查询线程
        Runnable noComplete = new Runnable() {
            @Override
            public void run() {
                readWriteLock.readLock().lock();

                try {
                    noCompleteFunc();
                } catch (Exception exc) {
                    exc.printStackTrace();
                } finally {
                    readWriteLock.readLock().unlock();
                }
            }
        };

        // 车次查询线程
        Runnable queryTrain = new Runnable() {
            @Override
            public void run() {
                readWriteLock.readLock().lock();

                try {
                    queryTrainFunc();
                } catch (Exception exc) {
                    exc.printStackTrace();
                } finally {
                    readWriteLock.readLock().unlock();
                }
            }
        };

        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10);

        executorService.scheduleWithFixedDelay(idle, 5, 6, TimeUnit.MINUTES);
        executorService.scheduleWithFixedDelay(noComplete, 1, 1, TimeUnit.MINUTES);
        executorService.scheduleWithFixedDelay(queryTrain, 5, 5, TimeUnit.SECONDS);
    }

    @Override
    public String queryTicket(JSONObject reqData, OkHttpClient okHttpClient) {
        String result = ticketReqService.queryTicket(reqData, okHttpClient);

        if (result == null) {
            result = "{\"httpstatus\":400}";
        }

        return result;
    }

    @Override
    public String queryNoComplete(JSONObject reqData, OkHttpClient okHttpClient) {
        String result = ticketReqService.queryNoComplete(reqData, okHttpClient);

        if (result == null) {
            result = "{\"httpstatus\":400}";
        }

        return result;
    }


    /*------------------------------------------------------------------------------------------*/
    /* 线程功能函数
    /*------------------------------------------------------------------------------------------*/
    // 空闲保持功能函数
    private void idleFunc() {
        JSONObject reqData = new JSONObject();
        reqData.put("fromStation", "YLZ");
        reqData.put("toStation", "NNZ");
        reqData.put("train_date", LogUtil.formatDate(new Date(), "yyyy-MM-dd"));

        for (OkHttpClient okHttpClient : ticketStore.getUserOkHttpClientList()) {
            try {
                queryTicket(reqData, okHttpClient);
                Thread.sleep(600);
            } catch (Exception exc) {
                exc.printStackTrace();
                readWriteLock.readLock().unlock();
            }
        }
    }

    // 未完成订单查询功能函数
    private void noCompleteFunc() {
        JSONObject reqData = new JSONObject();
        reqData.put("_json_att", "");

        for (TicketUser ticketUser : ticketStore.getTicketUserMap().values()) {
            if (ticketUser.getTicketStatus() != TicketStore.LOGINED) {
                continue;
            }

            JSONObject resJson = JSON.parseObject(queryNoComplete(reqData, ticketUser.getOkHttpClient()));

            if (resJson.containsKey("httpstatus")
                    && resJson.getInteger("httpstatus") == 200
                    && resJson.containsKey("data")
                    && resJson.getJSONObject("data").containsKey("orderDBList")) {
                // 存在未完成订单
                JSONArray noCompleteJsonList = resJson.getJSONObject("data").getJSONArray("orderDBList");

                for (int i = 0; i < noCompleteJsonList.size(); ++i) {
                    JSONObject noCompleteJson = noCompleteJsonList.getJSONObject(i);
                    TicketTask ticketTask = TicketFactroy.parseOrderDBList(noCompleteJson);

                    for (TicketTask wantTask : ticketUser.getTicketTaskMap().values()) {
                        if (wantTask.getTrainId().equals(ticketTask.getTrainId())
                                && wantTask.getPassengerId().equals(ticketTask.getPassengerId())
                                && wantTask.getStatus().equals("Confirm")) {
                            // 发送Email
                            String email = ticketUser.getEmail();
                            String emailText = MessageUtil.setTicketEmailText(ticketTask);
                            MessageUtil.sendEmailLnyt(email, "Lnyt", emailText);

                            wantTask.setLostTime(ticketTask.getLostTime());
                            wantTask.setStatus("Notified");
                            ticketTaskMapper.update(wantTask);
                        }
                    }
                }
            }

            for (TicketTask ticketTask : ticketUser.getTicketTaskMap().values()) {
                long curMils = (new Date()).getTime();

                if (ticketTask.getStatus().equals("Trying")) {
                    // 超过尝试下单时间 6 分钟，则认为下单失败
                    long oderMils = LogUtil.parseDateStr(ticketTask.getDate(), "yyyy-MM-dd hh:mm:ss").getTime();

                    if (curMils > (oderMils + 5 * 60 * 1000)) {
                        ticketTask.setStatus("Work");
                    }
//                } else if (ticketTask.getStatus().equals("Notified")) {
//                    // 订单超时
//                    long lostMils = LogUtil.parseDateStr(ticketTask.getLostTime(), "yyyy-MM-dd hh:mm:ss").getTime();
//
//                    if (curMils > lostMils) {
//                        ticketTask.setStatus("Work");
//                    }
                }
            }

            try {
                Thread.sleep(500);
            } catch (InterruptedException exc) {
                exc.printStackTrace();
            }
        }
    }

    // 查询下单功能函数
    private void queryTrainFunc() {
        OkHttpClient okHttpClient = new OkHttpClient();
        Map<String, TrainNumber> trainNumberMap = new HashMap<>();
//        System.out.println("Query run in " + (new Date()).toString());

        for (JSONObject reqData : ticketStore.getUserTaskReqList()) {
            String trainKey = reqData.getString("trainKey");

            // 判断日期是否超过今天
            long today = System.currentTimeMillis();
            long todayMil = today / (1000 * 3600 * 24) * (1000 * 3600 * 24) - TimeZone.getDefault().getRawOffset();
            long trainMil = LogUtil.parseDateStr(reqData.getString("train_date"), "yyyy-MM-dd").getTime();

            if (todayMil > trainMil) {
                continue;
            }

            String resQuery = queryTicket(reqData, okHttpClient);
            JSONObject resData = JSON.parseObject(resQuery);

            if (resData.getInteger("httpstatus") == 200) {
                JSONArray trainNumberData = resData.getJSONObject("data").getJSONArray("result");

                for (String item : trainNumberData.toJavaList(String.class)) {
                    TrainNumber trainNumber = TicketFactroy.createTrainNumber(item);
                    trainNumberMap.put(trainNumber.getTrainNumber(), trainNumber);
                }
            } else {
                continue;
            }

            // 判断是否有票
            TrainNumber trainNumber = trainNumberMap.get(reqData.getString("trainNumber"));

            if (trainNumber.getCanOrder().equals("Y")) {
                trainNumberMap.clear();
                String userKey = reqData.getString("userKey");
                OkHttpClient userHttpClient = ticketStore.getUserOkHttpClient(userKey);

                for (int i = 0; i < 3; ++i) {
                    resData = JSON.parseObject(queryTicket(reqData, userHttpClient));

                    if (resData.getInteger("httpstatus") == 200) {
                        JSONObject userData = new JSONObject();
                        String secretStr = CheckUtil.urlDecode(trainNumber.getSecretStr(), "UTF-8");

                        userData.put("key", trainKey);
                        userData.put("secretStr", secretStr);
                        userData.put("stationTrainCode", trainNumber.getTrainNumber());
                        userData.put("train_no", trainNumber.getTrainCdoe());
                        userData.put("fromStationTelecode", trainNumber.getFromStation());
                        userData.put("toStationTelecode", trainNumber.getToStation());
                        userData.put("train_location", trainNumber.getTrainLocation());

                        LogUtil.log4j2().info("try order");
                        setChanged();
                        notifyObservers(userData);
                        break;
                    }
                }
            }

            trainNumberMap.clear();

            try {
                Thread.sleep(200);
            } catch (InterruptedException exc) {
                exc.printStackTrace();
            }
        }
    }
}
